#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h> 
#include <stdint.h>

#pragma comment(lib, "ws2_32.lib") // 链接ws2_32库

#define PORT 8000
#define BUFFER_SIZE 2048

#define SYNC_CODE_1 (0xEB)
#define SYNC_CODE_2 (0x90)
#define TAIL_CODE   (0xAA)
#define CENTER_CODE_FILE_TRANS (0x81)

#pragma pack(push, 1)
typedef struct
{
    uint8_t sync_code_1;
    uint8_t sync_code_2;
    uint8_t func_code;
    uint16_t card_num;
    uint16_t data_len;
} center_protocol_head_t;

typedef struct
{
	uint8_t op_type;
	uint8_t uuid[40];
	uint8_t file_type;
	uint32_t file_time;
	uint32_t file_size;
	uint8_t md5[16];
	uint8_t file_name_len;
} write_file_active_header_t;

typedef struct
{
	uint8_t op_type;
	uint8_t uuid[40];
	uint8_t result;
    uint32_t cur_len;
    uint8_t md5[16];
} write_file_active_ack_header_t;

typedef struct
{
	uint8_t op_type;
	uint8_t uuid[40];
	uint32_t offset;
    uint32_t crc;
    uint16_t data_len;
} write_file_data_header_t;

typedef struct
{
	uint8_t op_type;
	uint8_t uuid[40];
	uint32_t offset;
    uint8_t result;
} write_file_data_ack_header_t;

#pragma pack(pop)

typedef enum
{
    CENTER_FILE_OP_RD_ACT = 1,
    CENTER_FILE_OP_RD_ACT_ACK,
    CENTER_FILE_OP_RD_DATA,
    CENTER_FILE_OP_RD_DATA_ACK,
    CENTER_FILE_OP_WR_ACT,
    CENTER_FILE_OP_WR_ACT_ACK,
    CENTER_FILE_OP_WR_DATA, 
    CENTER_FILE_OP_WR_DATA_ACK,
} center_file_op_e;


SOCKET serverSocket;
struct sockaddr_in clientAddr;
int clientAddrLen;
uint8_t g_recvbuf[BUFFER_SIZE];
uint8_t g_sendbuf[BUFFER_SIZE];
static int g_send_len;
FILE *g_cur_file;
uint32_t g_file_size = 0;

static uint8_t CharHi[] =
{
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
	0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
	0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
};

static uint8_t CharLo[]  =
{
	0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04,
	0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8,
	0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
	0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10,
	0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
    0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
	0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C,
	0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0,
	0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
 	0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
 	0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C,
	0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
	0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54,
	0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98,
 	0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
	0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40
};

uint16_t crc16Caluation(uint8_t * pData, int len)
{
	uint8_t inval;
	uint8_t crchi = 0xff;
	uint8_t crclo = 0xff;
	uint16_t i;

	if (len == 0){
		return 0;
	}

	for (i = 0; i < len; i++){
		inval = (uint8_t)(crchi ^ pData[i]);
		crchi = (uint8_t)(crclo ^ CharHi[inval]);
		crclo = CharLo[inval];
	}
	return (uint16_t)(crchi * 256 + crclo);
}

uint32_t little_endian_switch_big_endian_32(uint32_t value)
{
    // 使用位移和按位或运算符来重组字节顺序
    return ((value & 0xFF000000) >> 24) |
           ((value & 0x00FF0000) >> 8)  |
           ((value & 0x0000FF00) << 8)  |
           ((value & 0x000000FF) << 24);
}

uint16_t little_endian_switch_big_endian_16(uint16_t value)
{
    return ((value & 0xFF00) >> 8) |
           ((value & 0x00FF) << 8);
}

void set_protocol_head(uint8_t func_code, uint16_t card_num, uint16_t data_len)
{
	// printf("set head %d %d %d\r\n", func_code, card_num, data_len);
	center_protocol_head_t *head = (center_protocol_head_t *)g_sendbuf;
	head->sync_code_1 = SYNC_CODE_1;
	head->sync_code_2 = SYNC_CODE_2;
	head->func_code = func_code;
	head->card_num = little_endian_switch_big_endian_16(card_num);
	head->data_len = little_endian_switch_big_endian_16(data_len);
}

void send_files_req_ack()
{
    center_protocol_head_t * protocol_hd = (center_protocol_head_t *)&g_recvbuf[0];
    write_file_active_header_t *act_hd = (write_file_active_header_t *)&g_recvbuf[sizeof(center_protocol_head_t)];
    act_hd->file_time = little_endian_switch_big_endian_32(act_hd->file_time);
    act_hd->file_size = little_endian_switch_big_endian_32(act_hd->file_size);

    g_file_size = act_hd->file_size;

	memset(g_sendbuf, 0, sizeof(g_sendbuf));
	g_send_len = 0;
	set_protocol_head(CENTER_CODE_FILE_TRANS, *(uint16_t *)&protocol_hd->card_num, sizeof(write_file_active_ack_header_t));
	g_send_len += sizeof(center_protocol_head_t);

	write_file_active_ack_header_t *act_ack_hd = (write_file_active_ack_header_t *)&g_sendbuf[g_send_len];
	act_ack_hd->op_type = CENTER_FILE_OP_WR_ACT_ACK;
    memcpy(act_ack_hd->uuid, act_hd->uuid, sizeof(act_ack_hd->uuid));
    act_ack_hd->result = 0;
    act_ack_hd->cur_len = 0; //TODO: 读取文件获取当前长度
    //act_ack_hd->md5 =
	g_send_len += sizeof(write_file_active_ack_header_t);

	g_sendbuf[g_send_len] = TAIL_CODE;
	g_send_len++;

    act_ack_hd->cur_len = little_endian_switch_big_endian_32(act_ack_hd->cur_len);
	int ret = sendto(serverSocket, g_sendbuf, g_send_len, 0, (struct sockaddr *)&clientAddr, clientAddrLen);
	if(ret){
		printf("send_files_req_ack succ\n", ret);
	}
	else{
		printf("send_files_req_ack err[%d] \r\n", ret);
	}

    char filename[32] = {0};
    memcpy(filename, &g_recvbuf[sizeof(center_protocol_head_t) + sizeof(write_file_active_header_t)], act_hd->file_name_len);
    printf("filename:%s %d\n", filename, g_file_size);
    g_cur_file = fopen(filename, "wb");
    if (g_cur_file == NULL) {
        printf("Failed to open %s\n", filename);
    }
}

void send_files_data_ack()
{
    center_protocol_head_t * protocol_hd = (center_protocol_head_t *)&g_recvbuf[0];
    write_file_data_header_t *data_hd = (write_file_data_header_t *)&g_recvbuf[sizeof(center_protocol_head_t)];
    uint8_t *file_data = &g_recvbuf[sizeof(center_protocol_head_t) + sizeof(write_file_data_header_t)];

    // printf("op_type:%d offset:%d crc:%d data_len:%d\n", data_hd->op_type, data_hd->offset, data_hd->crc, data_hd->data_len);

    data_hd->offset = little_endian_switch_big_endian_32(data_hd->offset);
    data_hd->crc = little_endian_switch_big_endian_32(data_hd->crc);
    data_hd->data_len = little_endian_switch_big_endian_16(data_hd->data_len);
    // printf("ofs:%d crc:%d len:%d\n", data_hd->offset, data_hd->crc, data_hd->data_len);

    // 1. 校验crc
    uint32_t crc_calc = (uint32_t)crc16Caluation(file_data, data_hd->data_len);
    // printf("file_data:%02x %02x %02x %02x\n", file_data[0], file_data[1], file_data[2], file_data[3]);
    // printf("recv crc:%d calc crc:%d\n", data_hd->crc, crc_calc);
    if (data_hd->crc != crc_calc)
    {
        printf("ERR recv crc:%d calc crc:%d\n", data_hd->crc, crc_calc);
        return;
    }

    if ((data_hd->offset % 100*1024) == 0)
    {
        printf("recv:%d/%d\n", data_hd->offset, g_file_size);
    }

    // 2. 获取偏移，写入文件
    long int offset = data_hd->offset;
    int ret = fseek(g_cur_file, offset, SEEK_SET);
    if (ret != 0)
    {
        printf("ERR fseek %d(%d) failed, ret %d\n", data_hd->offset, offset, ret);
        return;
    }
    size_t len_writed = fwrite(file_data, 1, data_hd->data_len, g_cur_file);
    if (len_writed != data_hd->data_len)
    {
        printf("ERR len_writed %d != %d\n", len_writed, data_hd->data_len);
    }
    // printf("len_writed:%d\n", len_writed);

    // 3. 应答
	memset(g_sendbuf, 0, sizeof(g_sendbuf));
	g_send_len = 0;
	set_protocol_head(CENTER_CODE_FILE_TRANS, *(uint16_t *)&protocol_hd->card_num, sizeof(write_file_data_ack_header_t));
	g_send_len += sizeof(center_protocol_head_t);

	write_file_data_ack_header_t *data_ack_hd = (write_file_data_ack_header_t *)&g_sendbuf[g_send_len];
	data_ack_hd->op_type = CENTER_FILE_OP_WR_DATA_ACK;
    memcpy(data_ack_hd->uuid, data_hd->uuid, sizeof(data_ack_hd->uuid));
    data_ack_hd->offset = data_hd->offset + len_writed;
    data_ack_hd->result = 0;
	g_send_len += sizeof(write_file_data_ack_header_t);

	g_sendbuf[g_send_len] = TAIL_CODE;
	g_send_len++;

    // printf("req ofs:%d file_size:%d\n", data_ack_hd->offset, g_file_size);
    if (data_ack_hd->offset >= g_file_size)
    {
        printf("recv file finish, size:%d\n", data_ack_hd->offset);
        fclose(g_cur_file);
    }

    // printf("ACK ofs:%d\n", data_ack_hd->offset);
    // Sleep(20);

    data_ack_hd->offset = little_endian_switch_big_endian_32(data_ack_hd->offset);

	ret = sendto(serverSocket, g_sendbuf, g_send_len, 0, (struct sockaddr *)&clientAddr, clientAddrLen);
	if(ret){
		// printf("send_files_req_ack succ\n", ret);
	}
	else{
		printf("send_files_req_ack err[%d] \r\n", ret);
	}
}

static void file_trans_handler(void)
{
    const write_file_active_header_t *act_hd = (write_file_active_header_t *)&g_recvbuf[sizeof(center_protocol_head_t)];
    switch (act_hd->op_type)
    {
    case CENTER_FILE_OP_WR_ACT:
        send_files_req_ack();
        break;
    case CENTER_FILE_OP_WR_DATA:
        send_files_data_ack();
        break;
    default:
        break;
    }
}

int main() {
    WSADATA wsa;
    int wsOk = WSAStartup(MAKEWORD(2, 2), &wsa);
    if (wsOk != 0) {
        printf("Can't Initialize winsock! %d\n", wsOk);
        return -1;
    }

    serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (serverSocket == INVALID_SOCKET) {
        printf("Can't create socket! Error: %ld\n", WSAGetLastError());
        WSACleanup();
        return -1;
    }

    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(PORT);
    serverAddr.sin_addr.s_addr = INADDR_ANY;

    if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        printf("Bind failed with error code : %d", WSAGetLastError());
        closesocket(serverSocket);
        WSACleanup();
        return -1;
    }

    clientAddr;
    clientAddrLen = sizeof(clientAddr);

    while (1) {
        int bytesReceived = recvfrom(serverSocket, g_recvbuf, BUFFER_SIZE, 0, (struct sockaddr*)&clientAddr, &clientAddrLen);
        if (bytesReceived == SOCKET_ERROR) {
            printf("recvfrom failed with error: %d\n", WSAGetLastError());
            break;
        }
        g_recvbuf[bytesReceived] = '\0'; // Null-terminate the string
        // printf("Received from client: %02x %02x %02x %02x len:%d\n", g_recvbuf[0], g_recvbuf[1], g_recvbuf[2], g_recvbuf[3], bytesReceived);

        if((g_recvbuf[0] == 0xEB) && (g_recvbuf[1] == 0x90) && g_recvbuf[2] == 0x81) {
            file_trans_handler();
        }
    }

    closesocket(serverSocket);
    WSACleanup();
    return 0;
}

